﻿using System.Reflection;
using NLite.Internal;
using System;

namespace NLite.Reflection
{
	
	delegate object Getter(object target);

	/// <summary>
	/// 
	/// </summary>
	public static class GetterExtensions
	{
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TReturn"></typeparam>
        /// <param name="member"></param>
        /// <returns></returns>
        public static Func<object, T> GetGetter<T>(FieldInfo member)
        {
            Getter getter = null;
            if (member.DeclaringType.IsValueType)
                getter = target => member.GetValue(target);
            else
                getter = DynamicMethodFactoryProxy.Instance.CreateGetter(member);

            return target => (T)getter(target);

        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TReturn"></typeparam>
        /// <param name="member"></param>
        /// <returns></returns>
        public static Func<object, T> GetGetter<T>(PropertyInfo member)
        {
            Getter getter = null;
            if (member.DeclaringType.IsValueType)
                getter = target => member.GetValue(target,null);
            else
                getter = DynamicMethodFactoryProxy.Instance.CreateGetter(member);

            return target => (T)getter(target);

        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TReturn"></typeparam>
        /// <param name="member"></param>
        /// <returns></returns>
        public static Func<object, T> GetGetter<T>(MethodInfo member)
        {
            Getter getter = null;
            if (member.DeclaringType.IsValueType)
                getter = target => member.Invoke(target, new object[0]);
            else
                getter = DynamicMethodFactoryProxy.Instance.CreateGetter(member);

            return target => (T)getter(target);

        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="member"></param>
        /// <returns></returns>
        public static Func<object, TValue> GetGetter<TValue>(this MemberInfo member)
        {
            switch (member.MemberType)
            {
                case MemberTypes.Field: return GetGetter< TValue>(member as FieldInfo);
                case MemberTypes.Property: return GetGetter< TValue>(member as PropertyInfo);
                case MemberTypes.Method: return GetGetter<TValue>(member as MethodInfo);
            }

            return null;
        }

		/// <summary>
		/// 
		/// </summary>
		/// <param name="member"></param>
		/// <param name="target"></param>
		/// <returns></returns>
		public static object Get(this MemberInfo member, object target)
		{

			if (target != null && target.GetType().IsValueType)
			{
				switch (member.MemberType)
				{
						case MemberTypes.Field: return (member as FieldInfo).GetValue(target); 
						case MemberTypes.Property: return (member as PropertyInfo).GetValue(target, null);
						case MemberTypes.Method: return (member as MethodInfo).Invoke(target, new object[0]); 
				}

				return null;
			}
			else
			{
				try
				{
					return DynamicMethodFactoryProxy.Instance.CreateGetter(member)(target);
				}
				catch (TargetInvocationException ex)
				{
					
					throw ex.InnerException;
				}
				
			}
		}
	}
}
